using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Text.RegularExpressions;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using System.Management;
using System.Net.NetworkInformation;

namespace OdsController.Helpers {

	public class NetworkHelperObj {
		#region singleton fields
		// Static, VOLATILE variable to store single instance
		static volatile NetworkHelperObj _instance;
		// Static synchronization root object, for locking
		static object m_syncRoot = new object();
		#endregion singleton fields

		bool _ready = false;
		bool _online = false;
		IPAddress _pubIPAddress;
		IPAddress[] _myIPAddresses;
		string _pubIPCheckUrl;// = @"http://checkip.dyndns.org:8245/";
		string _pubIPCheckRegEx;// = @"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b";
		string _updateServerAddress;// = "update.ods.org";
		int _updateServerTCPPort; // =7070;

		// private constructor
		private NetworkHelperObj() {
			NetworkChange.NetworkAddressChanged += new NetworkAddressChangedEventHandler(NetworkChange_NetworkAddressChanged);
			NetworkChange.NetworkAvailabilityChanged += new NetworkAvailabilityChangedEventHandler(NetworkChange_NetworkAvailabilityChanged);
		}

		// Property to retrieve the only instance of the Singleton
		public static NetworkHelperObj Instance {
			get {
				if (_instance == null) {
					lock (m_syncRoot) {
						if (_instance == null) {
							_instance = new NetworkHelperObj();
						}
					}
				}
				return _instance;
			}
		}

		public void Init(bool checkNetworkStatus, string PubIPCheckUrl, string PubIPCheckRegEx, string updateServerAddress, int updateServerTCPPort) {
			_pubIPCheckUrl = PubIPCheckUrl;
			_pubIPCheckRegEx = PubIPCheckRegEx;
			_updateServerAddress = updateServerAddress;
			_updateServerTCPPort = updateServerTCPPort;

			if (checkNetworkStatus) {
				// refresh the ipaddress list
				CheckMyIPAddresses();
				// check to see if we can contact the ods update service
				CheckServerConnection();
				// re-detect my pub ip using webservice and regex
				CheckPubIpFromURL();
			}
			_ready = true;
		}

		/// <summary>
		/// Throws an exception if the NetworkHelperObj object is not initialized
		/// </summary>
		void CheckReadyStatus() {
			if (!_ready)
				throw new Exception("NetworkHelperObj object not initialized");
		}

		/// <summary>
		/// Retrieves the connection's public ip using a webpage and a regular expression (via WMI)
		/// </summary>
		void CheckPubIpFromURL() {
			WebRequest myRequest = WebRequest.Create(_pubIPCheckUrl);
			myRequest.Timeout = 30000;
			try {
				// Send request, get response, and parse out the IP address on the page.     
				using (WebResponse res = myRequest.GetResponse())
				using (Stream s = res.GetResponseStream())
				using (StreamReader sr = new StreamReader(s, Encoding.UTF8)) {
					string html = sr.ReadToEnd();
					Regex regex = new Regex(_pubIPCheckRegEx);

					IPAddress.TryParse(regex.Match(html).Value, out _pubIPAddress);
				}
			} catch {
				_pubIPAddress = null;
			}
		}

		/// <summary>
		/// refreshes the _pubIPAddress array with all the machine's ipv4 addresses
		/// </summary>
		/// <returns>string array with simple ip</returns>
		void CheckMyIPAddresses() {								// GetIPList - using WMI
			_myIPAddresses = new IPAddress[0];
			ArrayList aIps = new ArrayList();

			NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
			IPInterfaceProperties ipNicProps;
			for (int i = 0; i < nics.Length; i++) {
				if (!nics[i].Supports(NetworkInterfaceComponent.IPv4) || nics[i].NetworkInterfaceType == NetworkInterfaceType.Loopback) continue;
				ipNicProps = nics[i].GetIPProperties();
				if (null != ipNicProps && ipNicProps.UnicastAddresses.Count > 0)
					aIps.Add(ipNicProps.UnicastAddresses[0].Address);
			}


			//ManagementObjectSearcher query1 = new ManagementObjectSearcher("SELECT * FROM Win32_NetworkAdapterConfiguration");
			//ManagementObjectCollection queryCollection1 = query1.Get();

			//foreach (ManagementObject mo in queryCollection1) {
			//  if (mo["ipenabled"].ToString().ToUpper() == "TRUE") {		// o adaptador tem IP
			//    int i;
			//    for (i = 0; i < ((Array)mo["ipaddress"]).Length; i++) {
			//      if (((Array)mo["ipaddress"]).GetValue(i).ToString().Length > 0) {
			//        System.Diagnostics.Debug.WriteLine("odscontroller.GetIPList - " + ((Array)mo["ipaddress"]).GetValue(i));
			//        if (!((Array)mo["ipaddress"]).GetValue(i).ToString().StartsWith("0."))
			//          aIps.Add(((Array)mo["ipaddress"]).GetValue(i));// + "/" + ((Array)mo["ipsubnet"]).GetValue(i));
			//      }
			//    }
			//  }
			//}
			_myIPAddresses = new IPAddress[aIps.Count];
			aIps.CopyTo(_myIPAddresses);
		}

		/// <summary>
		/// sets _Online - tries to connect to the ods update server
		/// </summary>
		/// <returns>success</returns>
		void CheckServerConnection() {
			CheckReadyStatus();
			using (TcpClient tcpc = new TcpClient()) {
				try {
					tcpc.Connect(_updateServerAddress, _updateServerTCPPort);
					tcpc.Close();
				} catch {
					_online = false;
				}
			}
			_online = true;
		}

		#region NetworkChange Events
		public delegate void NetworkAvailabilityChangedDelegate(bool isNetworkAvailable);
		public NetworkAvailabilityChangedDelegate NetworkAvailabilityChanged;
		void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e) {
			if (null != NetworkAvailabilityChanged)
				NetworkAvailabilityChanged(e.IsAvailable);
		}

		public delegate void NetworkAddressChangedDelegate();
		public NetworkAddressChangedDelegate NetworkAddressChanged;
		void NetworkChange_NetworkAddressChanged(object sender, EventArgs e) {
			if (null != NetworkAddressChanged)
				NetworkAddressChanged();
		}
		#endregion NetworkChange Events

		#region public accessors
		/// <summary>
		/// Returns online status - if the update server can be contacted
		/// </summary>
		/// <param name="checkNow">forces the online check routine</param>
		/// <returns>Online status</returns>
		public bool GetOnline(bool checkNow) {
			if (checkNow)
				CheckServerConnection();
			return _online;
		}

		/// <summary>
		/// Returns the detected public IP address
		/// </summary>
		/// <param name="checkNow">forces the pub ip detection routine</param>
		/// <returns>IPAddress</returns>
		public IPAddress GetPubIPAddress(bool detectNow) {
			if (detectNow)
				CheckPubIpFromURL();
			return _pubIPAddress;
		}

		/// <summary>
		/// Returns the machine's ipv4 addresses
		/// </summary>
		/// <param name="checkNow">forces the machine's ipv4 addresses check routine</param>
		/// <returns>IPAddress[]</returns>
		public IPAddress[] GetIPAddresses(bool checkNow) {
			if (checkNow)
				CheckMyIPAddresses();
			return _myIPAddresses;
		}

		#endregion public accessors

		#region Public helper methods

		/// <summary>
		/// Validates an ip string
		/// </summary>
		/// <param name="ipstring">the ip address</param>
		/// <returns>success</returns>
		public bool IPValidate(string ipstring) {
			Regex reg = new Regex(@"(?<First>2[0-4]\d|25[0-5]|[01]?\d\d?)\.(?<Second>2[0-4]\d|25[0-5]|[01]?\d\d?)\.(?<Third>2[0-4]\d|25[0-5]|[01]?\d\d?)\.(?<Fourth>2[0-4]\d|25[0-5]|[01]?\d\d?)");
			Match m = reg.Match(ipstring);
			return m.Success;
		}

		#endregion Public helper methods
	}
}
